/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <sys/param.h>
#include <dirent.h>
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_http_server.h"
#include "esp_rom_gpio.h"
#include "soc/gpio_sig_map.h"

/* 存储上文件路径的最大长度 */
#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN)

/* 单个文件的最大尺寸。确保这个
 * 值与 upload_script.html 中设置的值相同 */
#define MAX_FILE_SIZE     (20 * 1024 * 1024) // 20 MB
#define MAX_FILE_SIZE_STR "20MB"

/* 临时缓冲区大小 */
#define SCRATCH_BUFSIZE 8192

/* 文件服务器数据结构，用于存储服务器运行时的状态和数据 */
struct file_server_data {
    /* 文件存储的基础路径 */
    char base_path[ESP_VFS_PATH_MAX + 1];

    /* 用于文件传输期间临时存储的缓冲区 */
    char scratch[SCRATCH_BUFSIZE];
};

static const char *TAG = "file_server"; // 日志标签

/* 处理函数：将对于 /index.html 的 GET 请求重定向到 /
 * 可以通过上传同名文件来覆盖此行为 */
static esp_err_t index_html_get_handler(httpd_req_t *req)
{
    // 设置HTTP状态码为307临时重定向
    httpd_resp_set_status(req, "307 Temporary Redirect");
    // 设置重定向的目标位置为根目录
    httpd_resp_set_hdr(req, "Location", "/");
    // 发送空响应体
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

/* 处理函数：响应嵌入在Flash中的图标文件。
 * 浏览器期望在 URI /favicon.ico 获取网站图标。
 * 可以通过上传同名文件来覆盖此行为 */
static esp_err_t favicon_get_handler(httpd_req_t *req)
{
    // 获取嵌入的favicon.ico文件的起始和结束地址（这些符号由链接器脚本生成）
    extern const unsigned char favicon_ico_start[] asm("_binary_favicon_ico_start");
    extern const unsigned char favicon_ico_end[] asm("_binary_favicon_ico_end");
    // 计算文件大小
    const size_t favicon_ico_size = (favicon_ico_end - favicon_ico_start);
    // 设置响应内容类型为 image/x-icon
    httpd_resp_set_type(req, "image/x-icon");
    // 发送文件内容
    httpd_resp_send(req, (const char *)favicon_ico_start, favicon_ico_size);
    return ESP_OK;
}

/* 处理函数：响应嵌入在Flash中的样式文件。
 * 浏览器期望在 URI /styles.css 获取网站样式。
 * 可以通过上传同名文件来覆盖此行为 */
static esp_err_t styles_get_handler(httpd_req_t *req)
{
    // 获取嵌入的styles.css文件的起始和结束地址
    extern const unsigned char styles_css_start[] asm("_binary_styles_css_start");
    extern const unsigned char styles_css_end[] asm("_binary_styles_css_end");
    const size_t styles_css_size = (styles_css_end - styles_css_start);
    // 设置响应内容类型为 text/css
    httpd_resp_set_type(req, "text/css");
    httpd_resp_send(req, (const char *)styles_css_start, styles_css_size);
    return ESP_OK;
}

/* 处理函数：响应嵌入在Flash中的上传页面。
 * 浏览器期望在 URI /upload.html 获取上传页面。
 * 可以通过上传同名文件来覆盖此行为 */
static esp_err_t upload_page_get_handler(httpd_req_t *req)
{
    // 获取嵌入的upload.html文件的起始和结束地址
    extern const unsigned char upload_html_start[] asm("_binary_upload_html_start");
    extern const unsigned char upload_html_end[] asm("_binary_upload_html_end");
    const size_t upload_html_size = (upload_html_end - upload_html_start);
    // 设置响应内容类型为 text/html
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, (const char *)upload_html_start, upload_html_size);
    return ESP_OK;
}

/* 处理函数：响应嵌入在Flash中的设置页面。
 * 浏览器期望在 URI /settings.html 获取设置页面。
 * 可以通过上传同名文件来覆盖此行为 */
static esp_err_t settings_page_get_handler(httpd_req_t *req)
{
    // 获取嵌入的settings.html文件的起始和结束地址
    extern const unsigned char settings_html_start[] asm("_binary_settings_html_start");
    extern const unsigned char settings_html_end[] asm("_binary_settings_html_end");
    const size_t settings_html_size = (settings_html_end - settings_html_start);
    // 设置响应内容类型为 text/html
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, (const char *)settings_html_start, settings_html_size);
    return ESP_OK;
}

/* 发送HTTP响应，内容是一个运行时生成的HTML页面，
 * 包含请求路径下所有文件和文件夹的列表。
 * 对于SPIFFS，当路径不是'/'时，由于SPIFFS不支持目录，会返回空列表 */
static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath)
{
    char entrypath[FILE_PATH_MAX]; // 存储完整条目路径
    char entrysize[16];            // 存储文件大小的字符串表示
    const char *entrytype;         // 条目类型："file" 或 "directory"

    struct dirent *entry;   // 目录条目结构
    struct stat entry_stat; // 文件状态结构

    // 打开目录
    DIR *dir = opendir(dirpath);
    const size_t dirpath_len = strlen(dirpath); // 基础路径长度

    /* 将基础路径复制到entrypath，用于后续构造完整路径 */
    strlcpy(entrypath, dirpath, sizeof(entrypath));

    if (!dir) {
        ESP_LOGE(TAG, "Failed to stat dir : %s", dirpath);
        /* 响应 404 Not Found */
        httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Directory does not exist");
        return ESP_FAIL;
    }

    /* 获取嵌入的文件列表页面HTML模板的第一部分 */
    extern const unsigned char file_list_1_start[] asm("_binary_file_list_1_html_start");
    extern const unsigned char file_list_1_end[] asm("_binary_file_list_1_html_end");
    const size_t file_list_1_size = (file_list_1_end - file_list_1_start);

    /* 获取嵌入的文件列表页面HTML模板的第二部分 */
    extern const unsigned char file_list_2_start[] asm("_binary_file_list_2_html_start");
    extern const unsigned char file_list_2_end[] asm("_binary_file_list_2_html_end");
    const size_t file_list_2_size = (file_list_2_end - file_list_2_start);

    // 发送HTML模板的第一部分（通常是头部和表格开始标签）
    httpd_resp_send_chunk(req, (const char *)file_list_1_start, file_list_1_size);

    /* 迭代所有文件/文件夹，获取它们的名称和大小 */
    while ((entry = readdir(dir)) != NULL) {
        // 根据d_type判断是目录还是文件
        entrytype = (entry->d_type == DT_DIR ? "directory" : "file");

        // 构造条目的完整路径：base_path + / + entry_name
        strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);
        // 获取条目的详细信息（大小等）
        if (stat(entrypath, &entry_stat) == -1) {
            ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entry->d_name);
            continue; // 失败则跳过此项
        }
        // 将文件大小转换为字符串
        sprintf(entrysize, "%ld", entry_stat.st_size);
        ESP_LOGI(TAG, "Found %s : %s (%s bytes)", entrytype, entry->d_name, entrysize);

        /* 发送包含文件名和大小的表格行的HTML代码块 */
        httpd_resp_sendstr_chunk(req, "<tr><td class=\"");
        httpd_resp_sendstr_chunk(req, entrytype); // 添加CSS类
        httpd_resp_sendstr_chunk(req, "\"><a href=\"");
        httpd_resp_sendstr_chunk(req, req->uri);      // 当前URI
        httpd_resp_sendstr_chunk(req, entry->d_name); // 条目名称
        if (entry->d_type == DT_DIR) {
            httpd_resp_sendstr_chunk(req, "/"); // 如果是目录，链接末尾加/
        }
        httpd_resp_sendstr_chunk(req, "\">");
        httpd_resp_sendstr_chunk(req, entry->d_name); // 链接显示的文本
        httpd_resp_sendstr_chunk(req, "</a></td><td>");
        httpd_resp_sendstr_chunk(req, entrysize); // 文件大小
        httpd_resp_sendstr_chunk(req, "</td><td>");
        // 添加删除按钮，并设置其filepath属性为条目的完整URI
        httpd_resp_sendstr_chunk(req, "<button class=\"deleteButton\" filepath=\"");
        httpd_resp_sendstr_chunk(req, req->uri);
        httpd_resp_sendstr_chunk(req, entry->d_name);
        httpd_resp_sendstr_chunk(req, "\">Delete</button>");
        httpd_resp_sendstr_chunk(req, "</td></tr>\n");
    }
    closedir(dir); // 关闭目录

    /* 发送HTML模板的第二部分（通常是表格结束标签和页面尾部） */
    httpd_resp_send_chunk(req, (const char *)file_list_2_start, file_list_2_size);

    /* 发送空块以指示HTTP响应完成 */
    httpd_resp_sendstr_chunk(req, NULL);
    return ESP_OK;
}

/* 宏：检查文件名是否以指定的扩展名结尾（不区分大小写） */
#define IS_FILE_EXT(filename, ext) \
    (strcasecmp(&filename[strlen(filename) - sizeof(ext) + 1], ext) == 0)

/* 根据文件扩展名设置HTTP响应内容类型 */
static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
{
    // 根据不同的文件扩展名设置对应的MIME类型
    if (IS_FILE_EXT(filename, ".pdf")) {
        return httpd_resp_set_type(req, "application/pdf");
    } else if (IS_FILE_EXT(filename, ".html")) {
        return httpd_resp_set_type(req, "text/html");
    } else if (IS_FILE_EXT(filename, ".png")) {
        return httpd_resp_set_type(req, "image/png");
    } else if (IS_FILE_EXT(filename, ".jpg") || IS_FILE_EXT(filename, ".jpeg")) {
        return httpd_resp_set_type(req, "image/jpeg");
    } else if (IS_FILE_EXT(filename, ".gif")) {
        return httpd_resp_set_type(req, "image/gif");
    } else if (IS_FILE_EXT(filename, ".ico")) {
        return httpd_resp_set_type(req, "image/x-icon");
    }
    /* 这里只列出了有限的几种类型 */
    /* 对于任何其他类型，总是设置为纯文本 */
    return httpd_resp_set_type(req, "text/plain");
}

/* 将完整路径复制到目标缓冲区，并返回指向路径的指针（跳过前面的基础路径） */
static const char *get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize)
{
    const size_t base_pathlen = strlen(base_path); // 基础路径长度
    size_t pathlen = strlen(uri);                  // URI长度

    // 查找URI中的查询字符串起始符'?'，如果找到则截断pathlen
    const char *quest = strchr(uri, '?');
    if (quest) {
        pathlen = MIN(pathlen, quest - uri);
    }
    // 查找URI中的片段标识符起始符'#'，如果找到则截断pathlen
    const char *hash = strchr(uri, '#');
    if (hash) {
        pathlen = MIN(pathlen, hash - uri);
    }

    // 检查完整路径（基础路径 + URI路径部分）是否会超出目标缓冲区
    if (base_pathlen + pathlen + 1 > destsize) {
        /* 完整路径字符串无法放入目标缓冲区 */
        return NULL;
    }

    /* 构造完整路径 (base_path + uri_path) */
    strcpy(dest, base_path);                        // 先复制基础路径
    strlcpy(dest + base_pathlen, uri, pathlen + 1); // 再追加URI的路径部分

    /* 返回指向路径的指针，跳过基础路径部分 */
    return dest + base_pathlen;
}

/* 处理函数：下载服务器上的文件 */
static esp_err_t download_get_handler(httpd_req_t *req)
{
    char filepath[FILE_PATH_MAX]; // 存储文件完整路径
    FILE *fd = NULL;              // 文件指针
    struct stat file_stat;        // 文件状态信息
    // 从URI提取文件名（得到指向完整路径中基础路径之后部分的指针）
    const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
                                             req->uri, sizeof(filepath));
    if (!filename) {
        ESP_LOGE(TAG, "Filename is too long");
        /* 响应 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
        return ESP_FAIL;
    }

    /* 如果名称以'/'结尾，则响应目录内容 */
    if (filename[strlen(filename) - 1] == '/') {
        return http_resp_dir_html(req, filepath);
    }

    // 获取文件状态信息
    if (stat(filepath, &file_stat) == -1) {
        /* 如果文件不在SPIFFS上，检查URI是否对应于某个硬编码的路径 */
        if (strcmp(filename, "/index.html") == 0) {
            return index_html_get_handler(req); // 提供内置index.html
        } else if (strcmp(filename, "/favicon.ico") == 0) {
            return favicon_get_handler(req); // 提供内置favicon.ico
        } else if (strcmp(filename, "/settings.html") == 0) {
            return settings_page_get_handler(req); // 提供内置settings.html
        } else if (strcmp(filename, "/upload.html") == 0) {
            return upload_page_get_handler(req); // 提供内置upload.html
        } else if (strcmp(filename, "/styles.css") == 0) {
            return styles_get_handler(req); // 提供内置styles.css
        }
        ESP_LOGE(TAG, "Failed to stat file : %s", filepath);
        /* 响应 404 Not Found */
        httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
        return ESP_FAIL;
    }

    // 以只读模式打开文件
    fd = fopen(filepath, "r");
    if (!fd) {
        ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
        /* 响应 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", filename, file_stat.st_size);
    // 根据文件扩展名设置Content-Type头
    set_content_type_from_file(req, filename);

    /* 获取指向临时缓冲区的指针 */
    char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
    size_t chunksize;
    do {
        /* 分块读取文件到临时缓冲区 */
        chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);

        if (chunksize > 0) {
            /* 将缓冲区内容作为HTTP响应块发送 */
            if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
                fclose(fd); // 关闭文件
                ESP_LOGE(TAG, "File sending failed!");
                /* 中止文件发送 */
                httpd_resp_sendstr_chunk(req, NULL); // 发送终止块
                /* 响应 500 Internal Server Error */
                httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
                return ESP_FAIL;
            }
        }

        /* 循环直到整个文件发送完毕 */
    } while (chunksize != 0);

    /* 发送完成后关闭文件 */
    fclose(fd);
    ESP_LOGI(TAG, "File sending complete");

    /* 发送一个空块以指示HTTP响应完成 */
    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}


/* 处理函数：上传文件到服务器 */
static esp_err_t upload_post_handler(httpd_req_t *req)
{
    char filepath[FILE_PATH_MAX]; // 存储文件完整路径
    FILE *fd = NULL;              // 文件指针
    struct stat file_stat;        // 文件状态信息

    /* 跳过URI中前面的"/upload"以获取文件名 */
    /* 注意 sizeof() 计算包括NULL终止符，因此要-1 */
    const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
                                             req->uri + sizeof("/upload") - 1, sizeof(filepath));
    if (!filename) {
        /* 响应 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
        return ESP_FAIL;
    }

    /* 文件名不能以'/'结尾 */
    if (filename[strlen(filename) - 1] == '/') {
        ESP_LOGE(TAG, "Invalid filename : %s", filename);
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename");
        return ESP_FAIL;
    }

    // 检查文件是否已存在
    if (stat(filepath, &file_stat) == 0) {
        ESP_LOGE(TAG, "File already exists : %s", filepath);
        /* 响应 400 Bad Request */
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
        return ESP_FAIL;
    }

    /* 文件大小不能超过限制 */
    if (req->content_len > MAX_FILE_SIZE) {
        ESP_LOGE(TAG, "File too large : %d bytes", req->content_len);
        /* 响应 400 Bad Request */
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
                            "File size must be less than " MAX_FILE_SIZE_STR "!");
        /* 返回失败以关闭底层连接，否则传入的文件内容将使套接字保持忙碌 */
        return ESP_FAIL;
    }

    // 以写入模式创建文件
    fd = fopen(filepath, "w");
    if (!fd) {
        ESP_LOGE(TAG, "Failed to create file : %s", filepath);
        /* 响应 500 Internal Server Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Receiving file : %s...", filename);

    /* 获取指向临时缓冲区的指针 */
    char *buf = ((struct file_server_data *)req->user_ctx)->scratch;
    int received; // 每次接收到的字节数

    /* 请求的内容长度给出了要上传的文件大小 */
    int remaining = req->content_len; // 剩余要接收的字节数

    while (remaining > 0) {
        ESP_LOGI(TAG, "Remaining size : %d", remaining);
        /* 将文件分部分接收到缓冲区中 */
        if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) {
            if (received == HTTPD_SOCK_ERR_TIMEOUT) {
                /* 如果是超时，重试 */
                continue;
            }

            /* 如果发生不可恢复的错误，
             * 关闭并删除未完成的文件 */
            fclose(fd);
            unlink(filepath); // 删除文件

            ESP_LOGE(TAG, "File reception failed!");
            /* 响应 500 Internal Server Error */
            httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
            return ESP_FAIL;
        }

        /* 将缓冲区内容写入存储中的文件 */
        if (received && (received != fwrite(buf, 1, received, fd))) {
            /* 无法将所有内容写入文件！
             * 存储空间可能已满？ */
            fclose(fd);
            unlink(filepath); // 删除不完整的文件

            ESP_LOGE(TAG, "File write failed!");
            /* 响应 500 Internal Server Error */
            httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
            return ESP_FAIL;
        }

        /* 跟踪剩余要上传的文件大小 */
        remaining -= received;
    }

    /* 上传完成后关闭文件 */
    fclose(fd);
    ESP_LOGI(TAG, "File reception complete");

    /* 重定向到根目录以查看更新的文件列表 */
    httpd_resp_set_status(req, "303 See Other");
    httpd_resp_set_hdr(req, "Location", "/");
    httpd_resp_sendstr(req, "File uploaded successfully");

    return ESP_OK;
}

/* 处理函数：从服务器删除文件 */
static esp_err_t delete_post_handler(httpd_req_t *req)
{
    char filepath[FILE_PATH_MAX]; // 存储文件完整路径
    struct stat file_stat;        // 文件状态信息

    /* 跳过URI中前面的"/delete"以获取文件名 */
    /* 注意 sizeof() 计算包括NULL终止符，因此要-1 */
    const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
                                             req->uri + sizeof("/delete") - 1, sizeof(filepath));
    if (!filename) {
        /* 响应 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
        return ESP_FAIL;
    }

    /* 文件名不能以'/'结尾 */
    if (filename[strlen(filename) - 1] == '/') {
        ESP_LOGE(TAG, "Invalid filename : %s", filename);
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename");
        return ESP_FAIL;
    }

    // 检查文件是否存在
    if (stat(filepath, &file_stat) == -1) {
        ESP_LOGE(TAG, "File does not exist : %s", filename);
        /* 响应 400 Bad Request */
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Deleting file : %s", filename);
    /* 删除文件 */
    unlink(filepath);

    /* 重定向到根目录以查看更新的文件列表 */
    httpd_resp_set_status(req, "303 See Other");
    httpd_resp_set_hdr(req, "Location", "/");
    httpd_resp_sendstr(req, "File deleted successfully");
    return ESP_OK;
}

// 声明外部函数，用于设置WiFi配置
extern esp_err_t app_wifi_set_wifi(char *mode, char *ap_ssid, char *ap_passwd, char *sta_ssid, char *sta_passwd);

/* 处理函数：从服务器获取并处理设置（GET请求，通常通过查询参数传递设置） */
static esp_err_t setting_get_handler(httpd_req_t *req)
{
    char query[160]; // 存储查询字符串
    // 用于存储解析出的WiFi参数
    char mode[16], ap_ssid[32], ap_passwd[32], sta_ssid[32], sta_passwd[32];

    // 从请求中获取URL查询字符串
    if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
        ESP_LOGI(TAG, "Query: %s", query);

        // 从查询字符串中解析各个参数
        if (httpd_query_key_value(query, "wifimode", mode, sizeof(mode)) == ESP_OK) {
            ESP_LOGI(TAG, "WIFI Mode: %s", mode);
        }

        if (httpd_query_key_value(query, "apssid", ap_ssid, sizeof(ap_ssid)) == ESP_OK) {
            ESP_LOGI(TAG, "AP SSID: %s", ap_ssid);
        }

        if (httpd_query_key_value(query, "appassword", ap_passwd, sizeof(ap_passwd)) == ESP_OK) {
            ESP_LOGI(TAG, "AP password: %s", ap_passwd);
        }

        if (httpd_query_key_value(query, "stassid", sta_ssid, sizeof(sta_ssid)) == ESP_OK) {
            ESP_LOGI(TAG, "STA SSID: %s", sta_ssid);
        }

        if (httpd_query_key_value(query, "stapassword", sta_passwd, sizeof(sta_passwd)) == ESP_OK) {
            ESP_LOGI(TAG, "STA password: %s", sta_passwd);
        }

        // 调用外部函数应用WiFi设置
        app_wifi_set_wifi(mode, ap_ssid, ap_passwd, sta_ssid, sta_passwd);
    } else {
        // 如果没有查询字符串，则返回设置页面
        return settings_page_get_handler(req);
    }

    // 发送成功响应，并提示需要重新连接
    httpd_resp_send(req, "Settings updated! Please reconnect!", HTTPD_RESP_USE_STRLEN);
    // 重启以应用新的WiFi模式配置
    esp_restart();
    return ESP_OK;
}

/* 处理函数：重置MSC (Mass Storage Class) */
static esp_err_t reset_msc_get_handler(httpd_req_t *req)
{
    // // 禁用USB VBUS以模拟断开连接
    // usbd_vbus_enable(false);
    // // 短暂延迟
    // vTaskDelay(20 / portTICK_PERIOD_MS);
    // // 重新启用USB VBUS以模拟重新连接
    // usbd_vbus_enable(true);

    // 发送成功响应
    httpd_resp_set_status(req, "200 OK");
    httpd_resp_set_type(req, "text/plain");// 设置响应类型为纯文本
    httpd_resp_sendstr(req, "MSC Reset Success");
    return ESP_OK;
}
static struct file_server_data *server_data = NULL; // 静态变量，保存服务器数据
static httpd_handle_t server = NULL;                 // HTTP服务器句柄
/* 启动文件服务器的函数 */
esp_err_t start_file_server(const char *base_path)
{

    /* 验证文件存储基础路径 */
    if (!base_path) { // 基础路径不能为NULL
        ESP_LOGE(TAG, "base path can't be NULL");
        return ESP_ERR_INVALID_ARG;
    }

    // 检查服务器是否已经启动
    if (server_data) {
        ESP_LOGE(TAG, "File server already started");
        return ESP_ERR_INVALID_STATE;
    }

    /* 为服务器数据分配内存 */
    server_data = calloc(1, sizeof(struct file_server_data));
    if (!server_data) {
        ESP_LOGE(TAG, "Failed to allocate memory for server data");
        return ESP_ERR_NO_MEM;
    }
    // 复制基础路径到服务器数据结构中
    strlcpy(server_data->base_path, base_path,
            sizeof(server_data->base_path));


    httpd_config_t config = HTTPD_DEFAULT_CONFIG(); // 使用默认配置初始化HTTP服务器配置

    /* 使用URI通配符匹配函数，以便让同一个处理函数响应多个
     * 匹配通配符方案的不同目标URI */
    config.uri_match_fn = httpd_uri_match_wildcard;

    ESP_LOGI(TAG, "Starting HTTP Server");
    // 启动HTTP服务器
    if (httpd_start(&server, &config) != ESP_OK) {
        ESP_LOGE(TAG, "Failed to start file server!");
        return ESP_FAIL;
    }

    /* 设置URI处理函数：重置MSC */
    httpd_uri_t reset_msc = {
        .uri = "/reset_msc", // 匹配 /reset_msc URI
        .method = HTTP_GET,
        .handler = reset_msc_get_handler,
        .user_ctx = server_data // 传递服务器数据作为上下文
    };
    httpd_register_uri_handler(server, &reset_msc);

    /* 设置URI处理函数：获取上传的文件（下载） */
    httpd_uri_t file_download = {
        .uri = "/*", // 匹配所有 /path/to/file 类型的URI (通配符)
        .method = HTTP_GET,
        .handler = download_get_handler,
        .user_ctx = server_data // 传递服务器数据作为上下文
    };
    httpd_register_uri_handler(server, &file_download);

    /* 设置URI处理函数：上传文件到服务器 */
    httpd_uri_t file_upload = {
        .uri = "/upload/*", // 匹配所有 /upload/path/to/file 类型的URI
        .method = HTTP_POST,
        .handler = upload_post_handler,
        .user_ctx = server_data // 传递服务器数据作为上下文
    };
    httpd_register_uri_handler(server, &file_upload);

    /* 设置URI处理函数：从服务器删除文件 */
    httpd_uri_t file_delete = {
        .uri = "/delete/*", // 匹配所有 /delete/path/to/file 类型的URI
        .method = HTTP_POST,
        .handler = delete_post_handler,
        .user_ctx = server_data // 传递服务器数据作为上下文
    };
    httpd_register_uri_handler(server, &file_delete);

    return ESP_OK; // 服务器启动成功
}

/* 停止文件服务器的函数 */
esp_err_t stop_file_server(void)
{
    esp_err_t ret = ESP_OK;

    if (server_data) {
        free(server_data); // 释放服务器数据内存
    }
    if (server)
    {
        ret=httpd_stop(server);
    }
    server_data = NULL; // 清空服务器数据指针
    server = NULL; // 清空服务器句柄
    return ret; 
}